package com.example.demoeasyexcelboot.commons;

import com.example.demoeasyexcelboot.annotation.FileCheck;
import com.example.demoeasyexcelboot.constants.FileType;
import lombok.extern.slf4j.Slf4j;
import org.apache.tomcat.util.http.fileupload.FileUploadException;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

/**
 * @author: FSL
 * @date: 2023/4/13
 * @description: 文件格式校验切面类
 */
@Component
@Slf4j
@Aspect
@ConditionalOnProperty(prefix = "file.check",name = "enabled",havingValue = "true")
public class FileCheckAop {

    @Before("@annotation(annotation)")
    public void before(JoinPoint joinPoint, FileCheck annotation) throws FileUploadException {
        FileType[] fileTypes = annotation.fileTypes();
        String message = annotation.message();
        FileCheck.CheckType type = annotation.type();

        if (fileTypes.length == 0) return;
        Object[] args = joinPoint.getArgs();
        Set<FileType> fileTypeSet = new HashSet<>(Arrays.asList(fileTypes));
        for (Object arg : args){
            if (arg instanceof MultipartFile){
                doCheck((MultipartFile)arg,type,fileTypeSet,message);
            } else if (arg instanceof MultipartFile[]) {
                doCheck((MultipartFile)arg,type,fileTypeSet,message);
            }
        }
        log.info("### 文件上传校验通过 ###");
    }

    private void doCheck(MultipartFile file, FileCheck.CheckType type, Set<FileType> fileTypeSet, String message) throws FileUploadException {
    switch (type){
        case SUFFIX:
            doCheckSuffix(file,fileTypeSet,message);
            break;
        case MAGIC_NUMBER:
            doCheckMagicNumber(file,fileTypeSet,message);
            break;
        default:
            doCheckSuffix(file,fileTypeSet,message);
            doCheckMagicNumber(file,fileTypeSet,message);
            break;
    }
    }

    private void doCheckSuffix(MultipartFile file,Set<FileType> fileTypeSet, String message) throws FileUploadException {
        String filename = file.getOriginalFilename();
        String fileSuffix = filename.substring(filename.lastIndexOf(".") + 1);
        for (FileType fileType : fileTypeSet){
            if (fileType.getSuffix().equalsIgnoreCase(fileSuffix)) return;
        }
        throw new FileUploadException(message);
    }

    private void doCheckMagicNumber(MultipartFile file, Set<FileType> fileTypeSet, String message) throws FileUploadException {
        String magicNumber= readMagicNumber(file);
        for (FileType fileType : fileTypeSet){
            if (fileType.getMagicNumber().startsWith(magicNumber.toUpperCase())){
                return;
            }
        }
        throw new FileUploadException(message);
    }

    private String readMagicNumber(MultipartFile file){
        try(InputStream is = file.getInputStream()) {
            byte[] fileByte = new byte[4];
            is.read(fileByte,0,4);
            return byteArray2Hex(fileByte);
        } catch (IOException e) {
            throw new RuntimeException("读取文件失败！");
        }
    }

    private String byteArray2Hex(byte[] fileByte) {
        StringBuilder builder = new StringBuilder();
        if (fileByte.length == 0) return null;
        for (byte datum : fileByte){
            int v = datum & 0xFF;
            String hv = Integer.toHexString(v);
            if (hv.length() < 2) builder.append(0);
            builder.append(hv);
        }
        return builder.toString();
    }
}
